﻿#include "videodecoderthread.h"
#include<QDebug>
#include <QVideoSurfaceFormat>

VideoDecoderThread::VideoDecoderThread(QObject * pParent, AVCodecContext *pCodecCtx, int duration,AVPixelFormat fmt):
    QThread(pParent)
{
    m_pCodecCtx = pCodecCtx;
    m_pCodecCtx = pCodecCtx;
    m_nDuration = duration;
    m_bRun = true;
    m_hw_fmt = fmt;
}

void VideoDecoderThread::addPacket(AVPacket *pPacket)
{
    m_lockPacket.lock();
    m_pPacket.push_back(av_packet_clone(pPacket));
    m_lockPacket.unlock();
}

void VideoDecoderThread::quit()
{
    m_bRun = false;
    wait();
    if(m_pFrameDec)
    {
        av_frame_free(&m_pFrameDec);
        m_pFrameDec = NULL;
    }
    if(m_pFrameCPU)
    {
        av_frame_free(&m_pFrameCPU);
        m_pFrameCPU = NULL;
    }
    if(m_OutBuf)
    {
        av_free(m_OutBuf);
        m_OutBuf = NULL;
    }
    if(m_pAVFrameRGB)
    {
        av_frame_free(&m_pAVFrameRGB);
        m_pAVFrameRGB = NULL;
    }
    if(m_pSwsCtx)
    {
        sws_freeContext(m_pSwsCtx);
        m_pSwsCtx = NULL;
    }
}

void VideoDecoderThread::playFrame(AVFrame *pFrame)
{
    if(pFrame->width != m_pCodecCtx->width || pFrame->height != m_pCodecCtx->height)
    {
        qDebug()<<"分辨率错误";
        return;
    }
    if(m_nWidth !=m_pCodecCtx->width || m_nHeight != m_pCodecCtx->height)
    {
        if(m_pFrameCPU)
        {
            av_frame_free(&m_pFrameCPU);
            m_pFrameCPU = NULL;
        }
        if(m_OutBuf)
        {
            av_free(m_OutBuf);
            m_OutBuf = NULL;
        }
        if(m_pAVFrameRGB)
        {
            av_frame_free(&m_pAVFrameRGB);
            m_pAVFrameRGB = NULL;
        }
        if(m_pSwsCtx)
        {
            sws_freeContext(m_pSwsCtx);
            m_pSwsCtx = NULL;
        }
        m_nWidth = m_pCodecCtx->width;
        m_nHeight = m_pCodecCtx->height;
    }
    if(m_pFrameCPU == NULL)
    {
        m_pFrameCPU = av_frame_alloc();
        m_pFrameCPU->width = m_nWidth;
        m_pFrameCPU->height = m_nHeight;
    }
    if(m_pAVFrameRGB == NULL)
    {
        m_pAVFrameRGB = av_frame_alloc();
        m_pAVFrameRGB->width = m_nWidth;
        m_pAVFrameRGB->height = m_nHeight;
        int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, m_nWidth, m_nHeight, 1);
        m_OutBuf = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
        av_image_fill_arrays(m_pAVFrameRGB->data, m_pAVFrameRGB->linesize, m_OutBuf, AV_PIX_FMT_RGB32, m_nWidth, m_nHeight, 1);

    }
    AVFrame * pSWFrame = NULL;
    if(pFrame->format == m_hw_fmt)
    {
        /* retrieve data from GPU to CPU */
        if ((av_hwframe_transfer_data(m_pFrameCPU, pFrame, 0)) < 0) {
            fprintf(stderr, "Error transferring the data to system memory\n");
        }
        pSWFrame = m_pFrameCPU;
    }
    else
    {
        pSWFrame = pFrame;
    }
    if(pSWFrame)
    {
        if(!m_pSwsCtx)
        {
            m_pSwsCtx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height, (AVPixelFormat)pSWFrame->format,
                                       m_pCodecCtx->width, m_pCodecCtx->height, AV_PIX_FMT_RGB32,
                                       SWS_BILINEAR, NULL, NULL, NULL);
        }
        if (m_pSwsCtx)
        {
            int ret = sws_scale(m_pSwsCtx, (uint8_t const * const *) pSWFrame->data, pSWFrame->linesize, 0,
                                m_nHeight, m_pAVFrameRGB->data, m_pAVFrameRGB->linesize);

            if (ret > 0)
            {
                QImage img((uint8_t *)m_pAVFrameRGB->data[0], m_nWidth, m_nHeight, QImage::Format_RGB32);
                emit updateFrame(&img);
            }
            else
            {
                qDebug() << "sws_scale failed with return code:" << ret;
            }
        }
        else
        {
            qDebug() << "Failed to initialize sws_getContext for format:" << pSWFrame->format;
        }
    }

}


AVPacket *VideoDecoderThread::getPacket()
{
    AVPacket * pPacket = NULL;
    if(m_pPacket.size() > 0)
    {
        m_lockPacket.lock();
        pPacket = m_pPacket.front();
        m_pPacket.pop_front();
        m_lockPacket.unlock();
    }
    return pPacket;
}

void VideoDecoderThread::run()
{
    while(m_bRun)
    {
        AVPacket * pPacket = getPacket();
        if(pPacket)
        {
            if(avcodec_send_packet(m_pCodecCtx,pPacket) == 0)
            {
                if(m_pFrameDec == NULL)
                    m_pFrameDec = av_frame_alloc();
                if(avcodec_receive_frame(m_pCodecCtx,m_pFrameDec) == 0)
                {
                    playFrame(m_pFrameDec);
                }
                av_packet_unref(pPacket);
            }
        }
        else
            QThread::msleep(1);
    }
}
